/* Hello World Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "driver/gpio.h"
#include "owb.h"
#include "bq2022a_led.h"

#define PROG_IO 16

static const uint8_t battery_clone_info[] = 
{
    0xed, 0x21, 0x4c, 0xe5, 0xed, 0xa9, 0x4b, 0x2e, 0x6a, 0xd4, 0xc6, 0xbe, 0x5e, 0xc8, 0xb6, 0xf5,
    0x90, 0x62, 0x72, 0xbe, 0x1c, 0x92, 0x58, 0x37, 0x2b, 0x9b, 0x29, 0x46, 0x6b, 0x01, 0x07, 0xc5,
    0x3c, 0x78, 0x88, 0x50, 0xe9, 0x9b, 0x63, 0x40, 0x26, 0x03, 0x9a, 0x55, 0xf7, 0xb9, 0x7f, 0x04,
    0x4a, 0x41, 0x83, 0x5d, 0x86, 0x3a, 0xe8, 0xf3, 0x17, 0x6c, 0x35, 0x8e, 0x11, 0x94, 0x13, 0x10,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};

static const uint8_t battery_clone_status[] =
{
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00
};

/*
static const uint8_t empty_info[] = 
{
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};*/

// ROM Commands in Command Level 1
#define BQ2022_COMMAND_READ_SERIALIZATION_ROM_AND_CRC	0x33
#define BQ2022_COMMAND_MATCH_SERIALIZATION_ROM		0x55
#define BQ2022_COMMAND_SEARCH_SERIALIZATION_ROM		0xF0
#define BQ2022_COMMAND_SKIP_SERIALIZATION_ROM		0xCC

// Memory Function Commands in Command Level 2
#define BQ2022_COMMAND_READ_MEMORY_FIELD_CRC		0xF0
#define BQ2022_COMMAND_READ_EPROM_STATUS		0xAA
#define BQ2022_COMMAND_READ_MEMORY_PAGE_CRC		0xC3
#define BQ2022_COMMAND_WRITE_MEMORY			0x0F
#define BQ2022_COMMAND_PRGRAMMING_PROFILE		0x99
#define BQ2022_COMMAND_WRITE_EPROM_STATUS		0x55

// Program Commands only in write memory or write status mode
#define BQ2022_PROGRAM_CONTROL				0x5A

#define BQ2022_ID_SAMSUNG_XWD				0x10139461
#define BQ2022_ID_GUANGYU				0x10139462
#define BQ2022_ID_SONY_XWD				0x10139463
#define BQ2022_ID_SAMSUNG_XWD_COSTDOWN			0x10139464
#define BQ2022_ID_LG_DESA				0x10139465
#define BQ2022_ID_SONY_FMT				0x10139466
#define BQ2022_ID_RUISHENG				0x10139467
#define BQ2022_ID_DELSA					0x8412E562
#define BQ2022_ID_AAC					0xAACAACAA
#define BQ2022_ID_COSLIGHT				0xDF0C7A62
#define BQ2022_ID_SAMSUNG_FMT				0xF40E9762

#define BQ2022_BATTERY_INFO_MAGIC			0xE54C21ED
typedef struct //1024 bit
{
	unsigned int magic;		//0xe54c21ed
	unsigned int pad1;		//0x2e4ba9ed
	unsigned int data1 : 8;		//0x68
	unsigned int pad2 : 24;		//0xbec6d4
	unsigned int pad3;		//0xf5b6c85e
	unsigned int pad4[4];		//0xbe726290, 0x3758921c, 0x46299b2b, 0xc507016b
	unsigned int pad5[4];		//0xbe726290, 0x3758921c, 0x46299b2b, 0xc507016b
	unsigned int pad6[3];		//0x5d83414a, 0xf3e83a86, 0x8e356c17
	unsigned int pad7 : 8;		//0x11
	unsigned int data2 : 24;	//0x101394
	unsigned int unused[16];	//ff...
} __attribute__((packed)) bq2022_battery_info;

static uint8_t battery_status[8];
static bq2022_battery_info battery_info = {0};

void hexdump(uint8_t *buf, uint32_t len)
{
    int i = 0;
    for(i = 0; i < len; i++) {
        printf("0x%02x, ", buf[i]);
        if( (i+1) % 16 == 0) {
            printf("\n");
        }
    }

    if(i%16 != 0) {
        printf("\n");
    }
}

void bq2022a_gpio_init(void)
{
    gpio_config_t led_io = 
    {
        .pin_bit_mask = (1<<PROG_IO),
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_DISABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE,
    };
    gpio_config(&led_io);
    gpio_set_level(PROG_IO,0);
}

owb_status bq2022a_show_status(OneWireBus * owb)
{
    owb_status status;
    bool is_present = false;
    uint8_t cmd[4];
    uint8_t crc;
    status = owb_reset(owb, &is_present);
    if((status != OWB_STATUS_OK)||(is_present!=true))
    {
        printf("reset status %d is_present: %s\n",status,is_present==true?"yes":"no");
        status = OWB_STATUS_DEVICE_NOT_RESPONDING;
        goto _ERROR;
    }
    /* rom comm byte + read comm byte + addr 2 bytes */
    cmd[0] = BQ2022_COMMAND_SKIP_SERIALIZATION_ROM;
    cmd[1] = BQ2022_COMMAND_READ_EPROM_STATUS;
    cmd[2] = 0x0;
    cmd[3] = 0x0;
    status = owb_write_bytes(owb,cmd,4);
    if(status != OWB_STATUS_OK)
        goto _ERROR;
    status = owb_read_byte(owb,&crc);
    if(status != OWB_STATUS_OK)
        goto _ERROR;
    if(crc != owb_crc8_bytes(0,&cmd[1],3))
    {
        printf(" crc failed ack %X != crc %X\n",crc,owb_crc8_bytes(0,&cmd[1],3));
        status = OWB_STATUS_CRC_FAILED;
        goto _ERROR;
    }
    status = owb_read_bytes(owb,(uint8_t*)&battery_status,sizeof(battery_status));
    if(status != OWB_STATUS_OK)
        goto _ERROR;
    status = owb_read_byte(owb,&crc);
    if(status != OWB_STATUS_OK)
        goto _ERROR;
    if(crc != owb_crc8_bytes(0,battery_status,sizeof(battery_status)))
    {
        printf(" crc failed ack %X != crc %X\n",crc,owb_crc8_bytes(0,&cmd[1],3));
        status = OWB_STATUS_CRC_FAILED;
        goto _ERROR;
    }
    printf("----------------------status info------------------------\n");
    hexdump((uint8_t*)&battery_status,sizeof(battery_status));
    printf("--------------------------end----------------------------\n");  
    _ERROR:
    return status;
}

owb_status bq2022a_show_info(OneWireBus * owb)
{
    owb_status status;
    bool is_present = false;
    uint8_t cmd[4];
    uint8_t crc;
    unsigned int w1_bq2022_battery_info_id = 0;
    status = owb_reset(owb, &is_present);
    if((status != OWB_STATUS_OK)||(is_present!=true))
    {
                bq2022a_mode_set(RED_LED_BLINK);
        printf("reset status %d is_present: %s\n",status,is_present==true?"yes":"no");
        status = OWB_STATUS_DEVICE_NOT_RESPONDING;
        goto _ERROR;
    }
    /* rom comm byte + read comm byte + addr 2 bytes */
    cmd[0] = BQ2022_COMMAND_SKIP_SERIALIZATION_ROM;
    cmd[1] = BQ2022_COMMAND_READ_MEMORY_FIELD_CRC;
    cmd[2] = 0x0;
    cmd[3] = 0x0;
    status = owb_write_bytes(owb,cmd,4);
    if(status != OWB_STATUS_OK)
    {
        bq2022a_mode_set(RED_LED_ON);
        printf("write cmd status %d\n",status);
        goto _ERROR;
    }
    status = owb_read_byte(owb,&crc);
    if(status != OWB_STATUS_OK)
    {
        bq2022a_mode_set(RED_LED_ON);
        printf("read crc status %d\n",status);
        goto _ERROR;
    }
    if(crc != owb_crc8_bytes(0,&cmd[1],3))
    {
        bq2022a_mode_set(RED_LED_ON);
        printf(" crc failed ack %X != crc %X\n",crc,owb_crc8_bytes(0,&cmd[1],3));
        status = OWB_STATUS_CRC_FAILED;
        goto _ERROR;
    }
    status = owb_read_bytes(owb,(uint8_t*)&battery_info,sizeof(battery_info));
    if(status != OWB_STATUS_OK)
    {
        bq2022a_mode_set(RED_LED_ON);
        printf("read battery info status %d\n",status);
        goto _ERROR;
    }
    status = owb_read_byte(owb,&crc);
    if(status != OWB_STATUS_OK)
    {
        bq2022a_mode_set(RED_LED_ON);
        printf("read crc status %d\n",status);
        goto _ERROR;
    }
    if(crc != owb_crc8_bytes(0,(uint8_t*)&battery_info,sizeof(battery_info)))
    {
        bq2022a_mode_set(RED_LED_ON);
        printf(" crc failed ack %X != crc %X\n",crc,owb_crc8_bytes(0,(uint8_t*)&battery_info,sizeof(battery_info)));
        status = OWB_STATUS_CRC_FAILED;
        goto _ERROR;
    }
    w1_bq2022_battery_info_id = battery_info.data1 | battery_info.data2 << 8;
    printf("battery magic is %X id is %X\n",battery_info.magic,w1_bq2022_battery_info_id);
    printf("----------------------battery info------------------------\n");
    hexdump((uint8_t*)&battery_info,sizeof(battery_info));
    printf("--------------------------end-----------------------------\n");
    _ERROR:
    return status;
}

owb_status bq2022a_flash(OneWireBus * owb)
{
    int32_t i;
    owb_status status;
    bool is_present = false;
    uint8_t cmd[4];
    uint8_t crc;
    if(memcmp(&battery_info,&battery_clone_info,sizeof(battery_info)) == 0)
    {
        bq2022a_mode_set(GREEN_LED_ON);
        printf("already Success\n");
        return OWB_STATUS_OK;
    }
    for(i=0;i<sizeof(battery_clone_info);i++)
    {
        if((((uint8_t*)&battery_info)[i] & battery_clone_info[i]) != battery_clone_info[i])
        {
            bq2022a_mode_set(GREEN_LED_BLINK);
            printf("OHH! battery chip flash wrong message! it's old?\n");
            return OWB_STATUS_HW_ERROR;
        }
    }
    for(i=0;i<16;i++)
    {
        if(memcmp(((uint8_t*)&battery_info)+(i*8),&battery_clone_info[i*8],8) == 0)
        {
            printf("same\n");
            continue;
        }
        status = owb_reset(owb, &is_present);
        if((status != OWB_STATUS_OK)||(is_present!=true))
        {
            bq2022a_mode_set(RED_LED_BLINK);
            printf("reset status %d is_present: %s\n",status,is_present==true?"yes":"no");
            status = OWB_STATUS_DEVICE_NOT_RESPONDING;
            goto _ERROR;
        }
        cmd[0] = BQ2022_COMMAND_SKIP_SERIALIZATION_ROM;
        cmd[1] = BQ2022_COMMAND_WRITE_MEMORY;
        cmd[2] = (i*8)&0xFF;
        cmd[3] = ((i*8)>>8)&0xFF;
        status = owb_write_bytes(owb,cmd,4);
        if(status != OWB_STATUS_OK)
        {
            bq2022a_mode_set(RED_LED_ON);
            printf("write cmd status %d\n",status);
            goto _ERROR;
        }
        status = owb_read_byte(owb,&crc);
        if(status != OWB_STATUS_OK)
        {
            bq2022a_mode_set(RED_LED_ON);
            printf("read crc status %d\n",status);
            goto _ERROR;
        }
        if(crc != owb_crc8_bytes(0,&cmd[1],3))
        {
            bq2022a_mode_set(RED_LED_ON);
            printf(" crc failed ack %X != crc %X\n",crc,owb_crc8_bytes(0,&cmd[1],3));
            status = OWB_STATUS_CRC_FAILED;
            goto _ERROR;
        }

        status = owb_write_bytes(owb,&battery_clone_info[i*8],8);
        if(status != OWB_STATUS_OK)
        {
            bq2022a_mode_set(RED_LED_ON);
            printf("write battery info status %d\n",status);
            goto _ERROR;
        }
        status = owb_read_byte(owb,&crc);
        printf("read crc status %d\n",status);
        if(status != OWB_STATUS_OK)
        {
            bq2022a_mode_set(RED_LED_ON);
            printf("read crc status %d\n",status);
            goto _ERROR;
        }
        if(crc != owb_crc8_bytes(0,&battery_clone_info[i*8],8))
        {
            bq2022a_mode_set(RED_LED_ON);
            printf(" crc failed ack %X != crc %X\n",crc,owb_crc8_bytes(0,&battery_clone_info[i*8],8));
            status = OWB_STATUS_CRC_FAILED;
            goto _ERROR;
        }
        status = owb_write_byte(owb,BQ2022_PROGRAM_CONTROL);
        if(status != OWB_STATUS_OK)
        {
            bq2022a_mode_set(RED_LED_ON);
            printf("write power status %d\n",status);
            goto _ERROR;
        }
        gpio_set_level(PROG_IO,1);
        vTaskDelay(50 / portTICK_PERIOD_MS);
        gpio_set_level(PROG_IO,0);
        vTaskDelay(100 / portTICK_PERIOD_MS);
    }
    _ERROR:
    gpio_set_level(PROG_IO,0);
    return status;
}

void app_main(void)
{
    // Create a 1-Wire bus, using the RMT timeslot driver
    OneWireBus * owb;
    owb_rmt_driver_info rmt_driver_info;
    bq2022a_gpio_init();
    bq2022a_led_init();
    owb = owb_rmt_initialize(&rmt_driver_info, 4, RMT_CHANNEL_1, RMT_CHANNEL_0);
    owb_use_crc(owb, true);  // enable CRC check for ROM code
    for(;;)
    {
        if(OWB_STATUS_OK == bq2022a_show_status(owb))
        {
           if(0 != memcmp(battery_clone_status,battery_status,sizeof(battery_clone_status)))
           {
                bq2022a_mode_set(RED_LED_ON);
                printf("battery chip status reg is invlid\n");
           }
        }
        if(OWB_STATUS_OK == bq2022a_show_info(owb))
        {
            bq2022a_flash(owb);
        }
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}
